home *** CD-ROM | disk | FTP | other *** search
/ Aminet 13 / Aminet 13 - August 1996.iso / Aminet / gfx / conv / WhirlGIF20.lha / WhirlGIF / src.original / whirlgif.c < prev    next >
C/C++ Source or Header  |  1996-03-03  |  17KB  |  792 lines

  1.   
  2. /*
  3.  * whirlgif.c
  4.  *
  5.  * Copyright (C) 1995,1996 by Kevin Kadow (kadokev@msg.net)
  6.  * 
  7.  * Based on txtmerge.c
  8.  * Copyright (C) 1990,1991,1992,1993 by Mark Podlipec. 
  9.  * All rights reserved.
  10.  *
  11.  * This software may be freely copied, modified and redistributed
  12.  * without fee provided that this copyright notice is preserved 
  13.  * intact on all copies and modified copies.
  14.  * 
  15.  * There is no warranty or other guarantee of fitness of this software.
  16.  * It is provided solely "as is". The author(s) disclaim(s) all
  17.  * responsibility and liability with respect to this software's usage
  18.  * or its effect upon hardware or computer systems.
  19.  *
  20.  */
  21.  /*
  22.   * Description:
  23.   *
  24.   * This program reads in a sequence of single-image GIF format files and
  25.   * outputs a single multi-image GIF file, suitable for use as an animation.
  26.   *
  27.   * TODO:
  28.   *
  29.   * More options for dealing with the colormap
  30.   *
  31.   * Eventually, I'd like to have this program compare the current image
  32.   * with the previous image and check to see if only a small section of
  33.   * the screen changed from the previous image. Worth a shot.
  34.   */
  35.  
  36.  /*
  37.   * Rev 2.00    05Feb96 Kevin Kadow
  38.   *    transparency, gif comments,
  39.   * Rev 1.10    29Jan96 Kevin Kadow
  40.   *    first release of whirlgif
  41.   *
  42.   * txtmerge:
  43.   * Rev 1.00    23Jul91    Mark Podlipec
  44.   *    creation
  45.   * Rev 1.01    08Jan92    Mark Podlipec
  46.   *     use all colormaps, not just 1st.
  47.   *
  48.   * 
  49.   */
  50. #define DA_REV 1.00
  51.  
  52. #include "whirlgif.h"
  53.  
  54. #define MAXVAL  4100            /* maxval of lzw coding size */
  55. #define MAXVALP 4200
  56.  
  57. /*
  58.  * Set some defaults, these can be changed on the command line
  59.  */
  60. unsigned int loop=DEFAULT_LOOP,loopcount=0,
  61.     use_colormap=DEFAULT_USE_COLORMAP,
  62.     debug_flag=0,
  63.     verbose=0;
  64.  
  65. int imagex = 0;
  66. int imagey = 0;
  67. int imagec = 0;
  68.  
  69. /* global settings for offset, transparency */
  70. Global global;
  71.  
  72. GIF_Color gif_cmap[256];
  73.  
  74. ULONG GIF_Get_Code();
  75. void GIF_Decompress();
  76. void GIF_Get_Next_Entry();
  77. void GIF_Add_To_Table();
  78. void GIF_Send_Data();
  79. void GIF_Clear_Table();
  80. void GIF_Screen_Header();
  81. void GIF_Image_Header();
  82. void GIF_Read_File();
  83. void GIF_Comment();
  84. void GIF_Loop();
  85. void GIF_GCL();
  86. void Calc_Trans();
  87. void set_offset();
  88.  
  89. GIF_Screen_Hdr gifscrn;
  90. GIF_Image_Hdr gifimage;
  91. GIF_Table table[MAXVALP];
  92.  
  93. ULONG root_code_size,code_size,CLEAR,EOI,INCSIZE;
  94. ULONG nextab;
  95. ULONG gif_mask[16] = {1,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,0,0};
  96. ULONG gif_ptwo[16] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,0,0};
  97.  
  98.  
  99. UBYTE gif_buff[MAXVALP];
  100. ULONG gif_block_size;
  101. int num_bits,bits;
  102.  
  103. int pic_i;
  104. char gif_file_name[BIGSTRING];
  105. int screen_was_last;
  106.  
  107.  
  108. void TheEnd()
  109. {
  110.  exit(0);
  111. }
  112.  
  113. void TheEnd1(p)
  114. char *p;
  115. {
  116.  fprintf(stderr,"%s",p);
  117.  TheEnd();
  118. }
  119.  
  120. Usage()
  121. {
  122.   fprintf(stderr,"\nUsage: whirlgif [-o outfile] [-loop [count]] [-time #delay]\n");
  123.   fprintf(stderr,"\t[ -i listfile] file1 [ -time #delay] file2 ...\n");
  124.   exit(0);
  125. }
  126.  
  127. main(argc,argv)
  128. int argc;
  129. char *argv[];
  130. {
  131.  FILE * infile, *fout;
  132.  char temp[BIGSTRING];
  133.  int ret,i;
  134.  int count=0;
  135.  
  136.  fprintf(stderr,"whirlgif Rev %2.2f (C) 1996 by Kevin Kadow\n",DA_REV);
  137.  fprintf(stderr,"                  (C) 1991,1992 by Mark Podlipec\n");
  138.  
  139.  if (argc < 2) Usage();
  140.  
  141.  /* set global values */
  142.  screen_was_last = FALSE;
  143.  global.trans.type=TRANS_NONE;
  144.  global.trans.valid=FALSE;
  145.  global.time=DEFAULT_TIME;
  146.  global.left=0;
  147.  global.top=0;
  148.  
  149.  
  150.  fout=stdout;
  151.  i = 1;
  152.  while( i < argc)
  153.  {
  154.   char *p;
  155.   p = argv[i];
  156.   /*fprintf(stderr,"Option: %s\n",p);*/
  157.   if ( (p[0] == '-') || (p[0] == '+') )
  158.   { 
  159.    ++p; /* strip off the - */
  160.    switch(p[0])
  161.    {
  162.     case 'v':    /* Give lots of information */
  163.             verbose++;
  164.             i++;
  165.         break;
  166.  
  167.     case 'd':    /* Debug mode */
  168.         debug_flag++;
  169.         i++;
  170.         fprintf(stderr,"DEBUG: Debug Level %d\n",debug_flag);
  171.         break;
  172.  
  173.     case 'l':    /* Enable looping */
  174.         loop=TRUE;
  175.         i++;
  176.         if(*argv[i] !='-') {
  177.           /* a loop count was given */
  178.           loopcount=atoi(argv[i++]);
  179.           if(verbose) fprintf(stderr,"Loop %d times\n",loopcount);
  180.           }
  181.         else {
  182.           /* default to infinite loop */
  183.           loopcount=0;
  184.           if(verbose) fputs("Looping enabled\n",stderr);
  185.           }
  186.         break;
  187.  
  188.     case 'u':    /* Use colormap? true or false */
  189.         i++;
  190.         if(atoi(argv[i]) || !strcmp("true",argv[i])) use_colormap=1;
  191.         else use_colormap=0;
  192.         i++;
  193.         break;
  194.  
  195.     case 't':    /* either time or transparent */
  196.         i++;
  197.         if(!strcmp("time",p)) {
  198.             /* Delay time in 1/100's of a second */
  199.             global.time=atoi(argv[i++]);
  200.             }
  201.         else if(!strncmp("trans",p,4)) Calc_Trans(argv[i++]);
  202.         break;
  203.  
  204.     case 'o':    /* Output file - send output to a given filename */
  205.         i++;
  206.         if(!strncmp("off",p,3)) set_offset(argv[i]);
  207.         else
  208.         /* It must be 'output, so do that */
  209.         if(NULL==(fout=fopen(argv[i],"w")))
  210.             {
  211.             fprintf(stderr,"Cannot open %s for output\n",argv[i]);
  212.             exit(1);
  213.             }
  214.         i++;
  215.         break;
  216.     case 'i':    /* input file - file with a list of images */
  217.         i++;
  218.         if(NULL != (infile=fopen(argv[i],"r"))) {
  219.             while(fgets(gif_file_name,BIGSTRING,infile)) {
  220.                 strtok(gif_file_name,"\n");
  221.                   if(!count) GIF_Read_File(fout,gif_file_name,1);
  222.                   else       GIF_Read_File(fout,gif_file_name,0);
  223.                   count++;
  224.                 }
  225.             fclose(infile);
  226.             }
  227.         else fprintf(stderr,"Cannot read list file %s\n",argv[i]);
  228.         i++;
  229.         break;
  230.     default: 
  231.         Usage();
  232.         exit(0);
  233.         break;
  234.    }
  235.    continue;
  236.   }
  237.   /* Not an option, must be the name of an input file */
  238.   if(!count) GIF_Read_File(fout,argv[i],1);
  239.   else       GIF_Read_File(fout,argv[i],0);
  240.   count++;
  241.   i++;
  242.  }
  243.  /* We're done with all the options, finish up */
  244.  if(count >0)
  245.   {
  246.   fputc(';',fout); /* image separator */
  247.   sprintf(temp,"whirlgif %2.2f (C) kadokev@msg.net. %d images",DA_REV,count);
  248.   GIF_Comment(fout,temp);
  249.   }
  250.  
  251.  fclose(fout);
  252.  fprintf(stderr,"Processed %d files.\n",count);
  253. }
  254.  
  255.  
  256. /*
  257.  * Read a GIF file, outputting to fname as we go.
  258.  * It would be faster to read and write the individual blocks,
  259.  * but eventually we'd like to optimize based on changes from
  260.  * previous images(ie only a small section of the image changed.
  261.  */
  262. void
  263. GIF_Read_File(fout,fname,first_image)
  264. FILE * fout;
  265. char *fname;
  266. int first_image;
  267. {
  268.  FILE *fp;
  269.  int ret,i,exit_flag;
  270.  
  271.  if ( (fp=fopen(fname,"r"))==0)
  272.  { 
  273.   fprintf(stderr,"Can't open %s for reading.\n",fname); 
  274.   TheEnd();
  275.  }
  276.  
  277.  GIF_Screen_Header(fp,fout,first_image);
  278.  
  279.  /*** read until  ,  separator */
  280.  do
  281.  {
  282.   i=fgetc(fp);
  283.   if ( (i<0) && feof(fp))
  284.   {
  285.    fclose(fp);
  286.    TheEnd1("GIF_Read_Header: Unexpected End of File\n");
  287.   }
  288.  } while(i != ',');
  289.  
  290.  if(first_image)
  291.   {
  292.    /* stuff we only do once */
  293.    if(loop) GIF_Loop(fout,loopcount);
  294.    }
  295.  if(global.time||(global.trans.type!=TRANS_NONE && global.trans.valid))
  296.     GIF_GCL(fout,global.time);
  297.  
  298.  fputc(',',fout); /* image separator */
  299.  
  300.  GIF_Image_Header(fp,fout,first_image);
  301.  
  302.  /*FOO*/
  303.  
  304.  /*** Setup ACTION for IMAGE */
  305.  
  306.  GIF_Decompress(fp,fout,0);
  307.  fputc(0,fout);  /* block count of zero */
  308.  
  309.  fclose(fp);
  310. }
  311.  
  312. void GIF_Decompress(fp,fout)
  313. FILE *fp,*fout;
  314. {
  315.  register ULONG code,old;
  316.  
  317.  pic_i = 0;
  318.  bits=0;
  319.  num_bits=0;
  320.  gif_block_size=0;
  321.     /* starting code size of LZW */
  322.  root_code_size=(fgetc(fp) & 0xff); fputc(root_code_size,fout);
  323.  GIF_Clear_Table();                /* clear decoding symbol table */
  324.  
  325.  code=GIF_Get_Code(fp,fout);
  326.  
  327.  if (code==CLEAR) 
  328.  {
  329.   GIF_Clear_Table(); 
  330.   code=GIF_Get_Code(fp,fout);
  331.  }
  332.  /* write code(or what it currently stands for) to file */
  333.  GIF_Send_Data(code);   
  334.  old=code;
  335.  code=GIF_Get_Code(fp,fout);
  336.  do
  337.  {
  338.   if (table[code].valid==1)    /* if known code */
  339.   {
  340.        /* send it's associated string to file */
  341.     GIF_Send_Data(code);
  342.     GIF_Get_Next_Entry(fp);       /* get next table entry (nextab) */
  343.     GIF_Add_To_Table(old,code,nextab);  /* add old+code to table */
  344.     old=code;
  345.   }
  346.   else      /* code doesn't exist */
  347.   {
  348.     GIF_Add_To_Table(old,old,code);   /* add old+old to table */
  349.     GIF_Send_Data(code);
  350.     old=code;
  351.   }
  352.   code=GIF_Get_Code(fp,fout);
  353.   if (code==CLEAR)
  354.   { 
  355.    GIF_Clear_Table();
  356.    code=GIF_Get_Code(fp,fout);
  357.    GIF_Send_Data(code);
  358.    old=code;
  359.    code=GIF_Get_Code(fp,fout);
  360.   }
  361.  } while(code!=EOI);
  362. }
  363.  
  364. void GIF_Get_Next_Entry(fp)
  365. FILE *fp;
  366. {
  367.    /* table walk to empty spot */
  368.  while(  (table[nextab].valid==1)
  369.        &&(nextab<MAXVAL)
  370.       ) nextab++;
  371.  /* 
  372.   * Ran out of space??!?  Something's roached 
  373.   */
  374.  if (nextab>=MAXVAL)    
  375.  { 
  376.   fprintf(stderr,"Error: GetNext nextab=%ld\n",nextab);
  377.   fclose(fp);
  378.   TheEnd();
  379.  }
  380.  if (nextab==INCSIZE)   /* go to next table size (and LZW code size ) */
  381.  {
  382.    /* fprintf(stderr,"GetNext INCSIZE was %ld ",nextab); */
  383.    code_size++; INCSIZE=(INCSIZE*2)+1;
  384.    if (code_size>=12) code_size=12;
  385. /*   fprintf(stderr,"<%ld>",INCSIZE); */
  386.  }
  387.  
  388. }
  389. /*  body is associated string
  390.     next is code to add to that string to form associated string for
  391.     index
  392. */     
  393.  
  394. void GIF_Add_To_Table(body,next,index)
  395. register ULONG body,next,index;
  396. {
  397.  if (index>MAXVAL)
  398.  { 
  399.   fprintf(stderr,"Error index=%ld\n",index);
  400.  }
  401.  else
  402.  {
  403.   table[index].valid=1;
  404.   table[index].data=table[next].first;
  405.   table[index].first=table[body].first;
  406.   table[index].last=body;
  407.  }
  408. }
  409.  
  410. void GIF_Send_Data(index)
  411. register int index;
  412. {
  413.  register int i,j;
  414.  i=0;
  415.  do         /* table walk to retrieve string associated with index */
  416.  { 
  417.   gif_buff[i]=table[index].data; 
  418.   i++;
  419.   index=table[index].last;
  420.   if (i>MAXVAL)
  421.   { 
  422.    fprintf(stderr,"Error: Sending i=%ld index=%ld\n",i,index);
  423.    TheEnd();
  424.   }
  425.  } while(index>=0);
  426.  
  427.  /* now invert that string since we retreived it backwards */
  428.  i--;
  429.  for(j=i;j>=0;j--)
  430.  {
  431.   /*pic[pic_i] = gif_buff[j] | gif_pix_offset;*/
  432.   pic_i++;
  433.  }
  434. }
  435.  
  436.  
  437. /* 
  438.  * initialize string table 
  439.  */
  440. void GIF_Init_Table()       
  441. {
  442.  register int maxi,i;
  443.  
  444. if (debug_flag) fprintf(stderr,"Initing Table...");
  445.  maxi=gif_ptwo[root_code_size];
  446.  for(i=0; i<maxi; i++)
  447.  {
  448.   table[i].data=i;   
  449.   table[i].first=i;
  450.   table[i].valid=1;  
  451.   table[i].last = -1;
  452.  }
  453.  CLEAR=maxi; 
  454.  EOI=maxi+1; 
  455.  nextab=maxi+2;
  456.  INCSIZE = (2*maxi)-1;
  457.  code_size=root_code_size+1;
  458. }
  459.  
  460.  
  461. /* 
  462.  * clear table 
  463.  */
  464. void GIF_Clear_Table()   
  465. {
  466.  register int i;
  467. if (debug_flag) fprintf(stderr,"Clearing Table...\n");
  468.  for(i=0;i<MAXVAL;i++) table[i].valid=0;
  469.  GIF_Init_Table();
  470. }
  471.  
  472. /*CODE*/
  473. ULONG GIF_Get_Code(fp,fout) /* get code depending of current LZW code size */
  474. FILE *fp,*fout;
  475. {
  476.  ULONG code;
  477.  register int tmp;
  478.  
  479.  while(num_bits < code_size)
  480.  {
  481.   /**** if at end of a block, start new block */
  482.   if (gif_block_size==0) 
  483.   {
  484.    tmp = fgetc(fp);
  485.    if (tmp >= 0 )
  486.    {
  487.     fputc(tmp,fout);
  488.     gif_block_size=(ULONG)(tmp);
  489.    }
  490.    else TheEnd1("EOF in data stream\n");
  491.   }
  492.  
  493.   tmp = fgetc(fp);   gif_block_size--;
  494.   if (tmp >= 0)
  495.   {
  496.    fputc(tmp,fout);
  497.    bits |= ( ((ULONG)(tmp) & 0xff) << num_bits );
  498.    num_bits+=8;
  499.   }
  500.   else TheEnd1("EOF in data stream\n");
  501.  }
  502.   
  503.  code = bits & gif_mask[code_size];
  504.  bits >>= code_size;
  505.  num_bits -= code_size; 
  506.  
  507.  
  508.  if (code>MAXVAL)
  509.  { 
  510.   fprintf(stderr,"\nError! in stream=%lx \n",code); 
  511.   fprintf(stderr,"CLEAR=%lx INCSIZE=%lx EOI=%lx code_size=%lx \n",
  512.                                            CLEAR,INCSIZE,EOI,code_size); 
  513.   code=EOI;
  514.  }
  515.  
  516.  if (code==INCSIZE)
  517.  {
  518.   if (code_size<12)
  519.   {
  520.    code_size++; INCSIZE=(INCSIZE*2)+1;
  521.   }
  522.   else if (debug_flag) fprintf(stderr,"<13?>"); 
  523.  }
  524.  
  525.  return(code);
  526. }
  527.  
  528.  
  529. /* 
  530.  * read GIF header 
  531.  */
  532. void GIF_Screen_Header(fp,fout,first_time)
  533. FILE *fp,*fout;
  534. int first_time;
  535. {
  536.  int temp,i;
  537.  
  538.  for(i=0;i<6;i++)
  539.  {
  540.   temp = fgetc(fp);
  541.   if(i==4 && temp == '7') temp='9';
  542.   if (first_time==TRUE) fputc(temp,fout);
  543.  }
  544.  
  545.  gifscrn.width  = GIF_Get_Short(fp,fout,first_time);
  546.  gifscrn.height = GIF_Get_Short(fp,fout,first_time);
  547.  temp=fgetc(fp);                 if (first_time==TRUE) fputc(temp,fout);
  548.  gifscrn.m       =  temp & 0x80;
  549.  gifscrn.cres    = (temp & 0x70) >> 4;
  550.  gifscrn.pixbits =  temp & 0x07;
  551.  
  552.  gifscrn.bc  = fgetc(fp);
  553.  if (first_time==TRUE) 
  554.     {
  555.     /* we really should set the background color to the transparent color */
  556.     fputc(gifscrn.bc,fout);
  557.     }
  558.  
  559.  temp=fgetc(fp);                 if (first_time==TRUE) fputc(temp,fout);
  560.  imagec=gif_ptwo[(1+gifscrn.pixbits)];
  561.  
  562.  if (verbose)
  563.   fprintf(stderr,"Screen: %ldx%ldx%ld m=%ld cres=%ld bkgnd=%ld pix=%ld\n",
  564.     gifscrn.width,gifscrn.height,imagec,gifscrn.m,gifscrn.cres,
  565.     gifscrn.bc,gifscrn.pixbits);
  566.  
  567.  if(global.trans.type==TRANS_RGB) global.trans.valid=0;
  568.  if (gifscrn.m)
  569.  {
  570.   for(i=0;i<imagec;i++)
  571.   {
  572.    gif_cmap[i].cmap.red   = temp = fgetc(fp); 
  573.            if (first_time==TRUE) fputc(temp,fout);
  574.    gif_cmap[i].cmap.green = temp = fgetc(fp); 
  575.            if (first_time==TRUE) fputc(temp,fout);
  576.    gif_cmap[i].cmap.blue  = temp = fgetc(fp); 
  577.            if (first_time==TRUE) fputc(temp,fout);
  578.  
  579.    if(global.trans.type==TRANS_RGB && !global.trans.valid)
  580.     if(global.trans.red==gif_cmap[i].cmap.red &&
  581.     global.trans.green==gif_cmap[i].cmap.green &&
  582.     global.trans.blue==gif_cmap[i].cmap.blue) {
  583.       if(debug_flag>1) fprintf(stderr," Transparent match at %d\n",i);
  584.         global.trans.map=i;
  585.     global.trans.valid=TRUE;
  586.     }
  587.   }
  588.  }
  589.  screen_was_last = TRUE;
  590. }
  591.  
  592. void GIF_Image_Header(fp,fout,first_time)
  593. FILE *fp,*fout;
  594. int first_time;
  595. {
  596.  int temp,tnum,i,r,g,b;
  597.  
  598.  gifimage.left   = GIF_Get_Short(fp,fout,1);
  599.  if(global.left) gifimage.left+=global.left;
  600.  
  601.  gifimage.top    = GIF_Get_Short(fp,fout,1);
  602.  if(global.top) gifimage.top+=global.top;
  603.  
  604.  gifimage.width  = GIF_Get_Short(fp,fout,1);
  605.  gifimage.height = GIF_Get_Short(fp,fout,1);
  606.  temp=fgetc(fp); 
  607.  
  608.  
  609.     
  610.  gifimage.i        = temp & 0x40;
  611.  gifimage.pixbits  = temp & 0x07;
  612.  gifimage.m        = temp & 0x80;
  613.  
  614.  /* this sets the local colormap bit to true */
  615.  if (screen_was_last && (first_time==FALSE)) temp |= 0x80;
  616.  
  617.  temp &= 0xf8;
  618.  temp |= gifscrn.pixbits;
  619.  fputc(temp,fout);
  620.  
  621.  imagex=gifimage.width;
  622.  imagey=gifimage.height;
  623.  tnum=gif_ptwo[(1+gifimage.pixbits)];
  624.  if (verbose)
  625.   fprintf(stderr,"Image: %ldx%ldx%ld (%d,%d) m=%ld i=%ld pix=%ld \n",
  626.     imagex,imagey,tnum,gifimage.left,gifimage.top,
  627.     gifimage.m,gifimage.i,gifimage.pixbits);
  628.  
  629.  /* if there is an image cmap, then use it */
  630.  
  631.  if (gifimage.m)
  632.  {
  633.   if(debug_flag) fprintf(stderr,"DEBUG:Transferring colormap of %d colors\n");
  634.   for(i=0;i<tnum;i++)
  635.   {
  636.    gif_cmap[i].cmap.red   = r = fgetc(fp);
  637.    gif_cmap[i].cmap.green = g = fgetc(fp);
  638.    gif_cmap[i].cmap.blue  = b = fgetc(fp);
  639.    fputc(r,fout);
  640.    fputc(g,fout);
  641.    fputc(b,fout);
  642.   }
  643.  }  /* else if screen was last not 1st time */
  644.  else if (screen_was_last && (first_time==FALSE))
  645.  {
  646.   if(debug_flag>1) fprintf(stderr,"DEBUG:Writing colormap of %d colors\n");
  647.   for(i=0;i<imagec;i++)
  648.   {
  649.    fputc(gif_cmap[i].cmap.red  ,fout);
  650.    fputc(gif_cmap[i].cmap.green,fout);
  651.    fputc(gif_cmap[i].cmap.blue ,fout);
  652.   }
  653.  }
  654.  screen_was_last = FALSE; 
  655. }
  656.  
  657.  
  658. /*
  659.  *
  660.  */
  661. int GIF_Get_Short(fp,fout,first_time)
  662. FILE *fp,*fout;
  663. int first_time;
  664. {
  665.  register int temp,tmp1;
  666.  temp=fgetc(fp);     if (first_time==TRUE) fputc(temp,fout);
  667.  tmp1=fgetc(fp);     if (first_time==TRUE) fputc(tmp1,fout);
  668.  return(temp|( (tmp1) << 8 ));
  669. }
  670.  
  671. void GIF_Comment(fout,string)
  672. FILE *fout;
  673. char *string;
  674. {
  675. if(!string || !strlen(string))
  676.         {
  677.         /* Bogus string */
  678.         if(debug_flag) fprintf(stderr,"GIF_Comment: invalid argument");
  679.         return;
  680.         }
  681. fputc(0x21,fout);
  682. fputc(0xF9,fout);
  683. fputs(string,fout);
  684. fputc(0,fout);
  685. }
  686.  
  687. /*
  688.  * Write a Netscape loop marker.
  689.  */
  690. void GIF_Loop(fout,repeats)
  691. FILE *fout;
  692. unsigned int repeats;
  693. {
  694. UBYTE low=0,high=0;
  695. if(repeats) {
  696.     /* non-zero repeat count- Netscape hasn't implemented this yet */
  697.     high=repeats / 256;
  698.     low=repeats % 256;
  699.     }
  700.  
  701. fputc(0x21,fout);
  702. fputc(0xFF,fout);
  703. fputc(0x0B,fout);
  704. fputs("NETSCAPE2.0",fout);
  705. fputc(0x03,fout);
  706. fputc(0x01,fout);
  707. fputc(0x00,fout);
  708.  
  709.  
  710. fputc(low,fout); /* the delay count - 0 for infinite */
  711. fputc(high,fout); /* second byte of delay count */
  712.  
  713. if(verbose) fprintf(stderr,"Wrote loop extension\n");
  714. }
  715.  
  716. /*
  717.  * GIF_GCL - add a Control Label to set an inter-frame delay value.
  718.  */
  719. void GIF_GCL(fout,delay)
  720. FILE * fout;
  721. unsigned int delay;
  722. {
  723. UBYTE low=0,high=0,flag=0;
  724. if(delay) {
  725.     /* non-zero delay, figure out low/high bytes */
  726.     high=delay / 256;
  727.     low=delay % 256;
  728.     }
  729.  
  730. fputc(0x21,fout);
  731. fputc(0xF9,fout);
  732. fputc(0x04,fout);
  733.  
  734. if(delay) flag |=0x80;
  735. if(global.trans.valid) flag |=0x01;
  736. fputc(flag,fout);
  737.  
  738. fputc(low,fout); /* the delay speed - 0 is instantaneous */
  739. fputc(high,fout); /* second byte of delay count */
  740.  
  741. fputc(global.trans.map,fout);
  742. fputc(0,fout);
  743. if(debug_flag>1) {
  744.   fprintf(stderr,"GCL: delay %d",delay);
  745.   if(global.trans.valid) fprintf(stderr," Transparent: %d",global.trans.map);
  746.   fputc('\n',stderr);
  747.   }
  748. }
  749.  
  750.  
  751. void Calc_Trans(string)
  752. char * string;
  753. {
  754. if(string[0] != '#') {
  755.   global.trans.type=TRANS_MAP;
  756.   global.trans.map=atoi(string);
  757.   global.trans.valid=1;
  758.   }
  759. else
  760.   {
  761.   /* it's an RGB value */
  762.   int r,g,b;
  763.   string++;
  764.   if(3==sscanf(string,"%2x%2x%2x",&r,&g,&b)) {
  765.     global.trans.red=r;
  766.     global.trans.green=g;
  767.     global.trans.blue=b;
  768.     global.trans.type=TRANS_RGB;
  769.     if(debug_flag) fprintf(stderr,"Transparent RGB=(%x,%x,%x)\n",r,g,b);
  770.     }
  771.   }
  772. if(debug_flag) fprintf(stderr,"DEBUG:Calc_trans is %d\n",global.trans.type);
  773. }
  774.  
  775. void set_offset(string)
  776. char * string;
  777. {
  778. char *off_x,*off_y;
  779. off_x=(char *) strtok(string,",");
  780. off_y=(char *)strtok((char *)NULL,",");
  781. if(off_x && off_y) {
  782.     /* set the offset */
  783.     global.left=atoi(off_x);
  784.     global.top=atoi(off_y);
  785.     if(debug_flag>1) fprintf(stderr,"Offset changed to %d,%d\n",
  786.         global.left,global.top);
  787.     return;
  788.     }
  789. if(debug_flag>1) fprintf(stderr,"Couldn't parse offset values.\n");
  790. exit();
  791. }
  792.